<?PHP
/*
 * $Id: barge-helper.classes.php 139 2009-12-16 15:29:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\helper;

use \Exception;
use barge\utils\ArrayList;
use barge\utils\Bean;
use barge\utils\Date;
use barge\utils\HashMap;
use barge\utils\Month;

/**
 * カレンダー
 *
 * @version 1.00
 * @author Kenji Namba
 */
class Calendar extends Bean
{
	/**
	 * @var Date
	 */
	protected $today;

	/**
	 * @var Month
	 */
	protected $currentMonth;

	/**
	 * @var HashMap
	 */
	private $cache;

	/**
	 * @var HashMap
	 */
	private $loadCache;

	/**
	 * @var int
	 */
	private $startOfWeek = 1;

	/**
	 * @param Month $curretnMonth アクティブな月
	 * @param Date $today
	 */
	public function __construct(Month $curretnMonth = null, Date $today = null)
	{
		$this->today = new Date() ?: $today;
		$this->currentMonth = new Month() ?: $curretnMonth;
		$this->cache = new HashMap();
		$this->loadCache = new HashMap();
		$this->reloadAsCache();
	}

	/**
	 * カレンダーの読み込み処理
	 */
	private function reloadAsCache()
	{
		if (! $this->loadCache->contains($this->currentMonth)) {
			$weeks = new ArrayList();
			$date = $this->currentMonth->getFirstDate();
			$n = 0;
			while ($date->getMonths() == $this->currentMonth->getMonths()) {
				$weeks->add($this->createWeeks($this->currentMonth, clone $date, $this->startOfWeek));
				$date->add(Date::DATE, 7);
			}
			$this->loadCache->put($this->currentMonth, $weeks);
		}
	}

	/**
	 * @param Month $month
	 * @param Date $date
	 * @param int $start
	 * @return unknown
	 */
	private function createWeeks(Month $month, Date $date, $start)
	{
		$baseDate = $date;
		$i = 0;
		while ($baseDate->getWeeks() == $start) {
			$baseDate->add(Date::DATE, -1);
		}

		$week = new ArrayList();
		for ($i = 0; $i < 7; $i++) {
			$dayObj = new CalendarDate(clone $baseDate);
			// 日付へのインデックスを作成
			$this->cache->put($dayObj->getDate()->getTimeValue(), $dayObj);
			$week->add($dayObj);
			$baseDate->add();
		}
		return $week;
	}

	/**
	 * @param Date|int $day
	 * @param mixed $item
	 */
	public function setItem($date, $item)
	{
		$date = ($date instanceof Date ? $date->getTimeValue() : $date);
		if ($this->cache->contains($date)) {
			$this->cache->get($date)->getItems()->add($item);
		}
	}

	/**
	 * @return Month
	 */
	public function getCurrentMonth()
	{
		return $this->currentMonth;
	}

	/**
	 * @return Date
	 */
	public function getToday()
	{
		return $this->today;
	}

	/**
	 * @return ArrayList
	 */
	public function getWeeks()
	{
		return $this->loadCache->get($this->currentMonth);
	}

	public function nextMonth()
	{
		$this->currentMonth->addMonth();
		$this->reloadAsCache();
	}

	public function previousMonth()
	{
		$this->currentMonth->addMonth(-1);
		$this->reloadAsCache();
	}

	public function setStartOfWeeks($wday)
	{
		$this->startOfWeek = $wday;
	}
}

/**
 * @version 1.0
 * @author Kenji Namba
 */
class CalendarDate extends Bean
{
	protected $today;

	protected $isGregolinic;

	/**
	 * @var Date
	 */
	protected $date;

	/**
	 * @var ArrayList
	 */
	protected $items;


	//public $items = array();
	public function __construct(Date $date, ArrayList $items = null)
	{
		$this->date = $date;
		$this->today = false;
		$this->items = ($items === null ? new ArrayList() : $items);
	}

	/**
	 * @return Date
	 */
	public function getDate()
	{
		return $this->date;
	}

	public function setToday($flag)
	{
		$this->today = $flag;
	}

	public function getItems()
	{
		return $this->items;
	}

	public function getFirstItem()
	{
		if ($this->items->hasElement()) {
			return $this->items->get(0);
		} else {
			return null;
		}
	}
}

/**
 * リストのページャーサポート
 *
 * <pre>
 * ≪ 前へ | 1 | 2 | 3 | 次へ ≫
 * </pre>
 *
 * @version 1.2 2009/02/21
 * @author Kenji Namba
 * @see Bean
 */
class Paginator extends Bean
{
	const DEFAULT_MAXROWS = 10;
	private $items;
	private $offset;
	private $maxrows;
	private $totalcount;
	private $itemoffseted = false;

	/**
	 * @param ArrayList $items
	 * @param int $offset
	 * @param int $maxrows
	 * @param bool $itemoffseted
	 * @param int $totalcount
	 * @return Paginator
	 */
	public static function wrapOffseted(ArrayList $items, $offset = 1, $maxrows = self::DEFAULT_MAXROWS, $itemoffseted = false, $totalcount = null)
	{
		return new self($items, $offset, $maxrows, $itemoffseted, $totalcount !== null ? $totalcount : $items->size());
	}

	/**
	 * @param ArrayList $items
	 * @param int $page
	 * @param int $maxrows
	 * @param bool $itemoffseted
	 * @param int $totalcount
	 * @return Paginator
	 */
	public static function wrapPaging(ArrayList $items, $page = 1, $maxrows = self::DEFAULT_MAXROWS, $itemoffseted = false, $totalcount = null)
	{
		$offset = $page;
		return new self($items, $offset, $maxrows, $itemoffseted, $totalcount !== null ? $totalcount : $items->size());
	}

	public function __construct(ArrayList $items, $offset = 1, $maxrows = self::DEFAULT_MAXROWS, $itemoffseted = false, $totalcount = null)
	{
		$this->items = $items;
		$this->offset = $offset;
		$this->maxrows = $maxrows;
		$this->totalcount = $totalcount !== null ? $totalcount : $items->size();
		$this->itemoffseted = $itemoffseted;
	}
	public function isItemOffseted()
	{
		return $this->itemoffseted;
	}
	public function getTotalCount()
	{
		return $this->totalcount;
	}
	public function getMaxRows()
	{
		return $this->maxrows;
	}
	public function getHasPrev()
	{
		return ($this->offset > 1);
	}
	public function pageNext()
	{
		if ($this->getHasNext() === false) {
			$message = "無効なページ操作です。";
			throw new Exception($message);
		}
		$this->offset += $this->maxrows;

	}
	public function pagePrev()
	{
		if ($this->getHasPrev() === false) {
			$message = "無効なページ操作です。";
			throw new Exception($message);
		}
		$this->offset -= $this->maxrows;
	}

	/**
	 * @return bool
	 */
	public function getHasNext()
	{
		return (($this->offset + $this->maxrows) <= $this->totalcount);
	}

	/**
	 * @return Page
	 */
	public function getCurrentPage()
	{
		$no = (int)ceil($this->offset / $this->maxrows);
		return new Page($no, $this->offset,
			min(($this->offset + $this->maxrows - 1), $this->totalcount),
			true);
	}

	/**
	 * @return Page
	 */
	public function getNextPage()
	{
		if ($this->getHasNext() === false) {
			$message = "無効なページ操作です。";
			throw new Exception($message);
		}
		$nextoffset = $this->offset + $this->maxrows;
		$no = (int)ceil($nextoffset / $this->maxrows);
		return new Page($no, $nextoffset,
			min(($nextoffset + $this->maxrows - 1), $this->totalcount),
			false);
	}

	/**
	 * @return Page
	 */
	public function getPrevPage()
	{
		if ($this->getHasPrev() === false) {
			$message = "無効なページ操作です。";
			throw new Exception($message);
		}
		$prevoffset = max($this->offset - $this->maxrows, 1);
		$no = (int)ceil($prevoffset / $this->maxrows);
		return new Page($no, $prevoffset,
			min(($prevoffset + $this->maxrows - 1), $this->totalcount),
			false);
	}

	public function getPages()
	{
		$currentpage = (int)ceil($this->offset / $this->maxrows);
		$pages = new ArrayList();
		$totalpages = $this->getTotalPage();
		for ($i = 1; $i <= $totalpages; $i++) {
			$start = ($i * $this->maxrows) - $this->maxrows + 1;
			$pages->add(
				new Page($i,
				$start,
				min(($start + $this->maxrows - 1), $this->totalcount),
				$i == $currentpage));
		}
		return $pages;
	}
	public function getCurrentItems()
	{
		return $this->items;
	}
	public function getTotalPage()
	{
		return (int)ceil($this->totalcount / $this->maxrows);
	}

//	private function getEndOffset($offset, )
//	{
//		return (int)ceil($this->totalcount / $this->maxrows);
//	}
}

class Page extends Bean
{
	private $no;
	private $startoffset;
	private $endoffset;
	private $current;

	/**
	 * @param int $no
	 * @param int $startoffset
	 * @param int $endoffset
	 * @param bool $current
	 */
	public function __construct($no, $startoffset, $endoffset, $current)
	{
		$this->no = $no;
		$this->startoffset = $startoffset;
		$this->endoffset = $endoffset;
		$this->current = $current;
	}

	public function getNo()
	{
		return $this->no;
	}

	public function getStartOffset()
	{
		return $this->startoffset;
	}

	public function getEndOffset()
	{
		return $this->endoffset;
	}

	public function isCurrent()
	{
		return $this->current;
	}

}

/**
 * リストのページャーサポート
 *
 * <pre>
 * ≪ 前へ | 1 | 2 | 3 | 次へ ≫
 * </pre>
 *
 * @version 1.2 2009/02/21
 * @author Kenji Namba
 * @see Bean
 */
class PaginatorOld extends Bean
{
	/**
	 * @var int
	 */
	protected $page;

	/**
	 * @var int １ページ中の項目数
	 */
	protected $pageSize = 20;

	/**
	 * @var int 全項目数
	 */
	protected $totalCount = 0;

	/**
	 * @var ArrayList
	 */
	protected $list;

	/**
	 * @var int ページグループ数
	 */
	protected $groupsPage;


	//	public function setGroupsPage($value)
	//	{
	//		$this->groupsPage = $value;
	//	}


	/**
	 * コンストラクタ
	 *
	 * @param ArrayList $list
	 * @param int $size
	 * @param int $groupsPage
	 * @param int $total
	 */
	public function __construct(ArrayList $list, $total = null)
	{
		$this->list = $list;
		$this->page = 1;

		if ($total !== null) {
			$this->totalCount = $total;
		} else {
			$this->totalCount = $list->size();
		}
	}

	/**
	 * @return ArrayList
	 */
	public function getPageList()
	{
		$pageList = new ArrayList();
		$totalPages = $this->getTotalPage();
		$startPage = 0;
		$endPage = 0;
		if ($this->groupsPage === null) {
			$startPage = 1;
			$endPage = $totalPages;
		} else {
			$startPage = ($this->groupsPage * ceil($this->page / $this->groupsPage) -
			($this->groupsPage - 1));
			$endPage = (($startPage + $this->groupsPage -1) > $totalPages ? $totalPages : ($startPage + $this->groupsPage -1))  ;
		}


		for ($p = $startPage; $p <= $endPage; $p++) {
			$pageList->add($p);
		}

		return $pageList;
	}

	/**
	 * @return ArrayList
	 */
	public function getCurrentList()
	{
		return $this->list->subSequence(($this->page - 1) * $this->pageSize, $this->pageSize);
	}

	/**
	 * @return bool
	 */
	public function getCurrentIndex()
	{
		//		return ($this->page < $this->getTotalPage());
	}

	/**
	 * @return bool
	 */
	public function setCurrentIndex()
	{
		//		return ($this->page < $this->getTotalPage());
	}

	/**
	 * @return bool
	 */
	public function getHasNextPage()
	{
		return ($this->page < $this->getTotalPage());
	}

	/**
	 * @return bool
	 */
	public function getHasPage($page)
	{
		return ($page >= 0 && $page <= $this->getTotalPage());
	}

	/**
	 * @return bool
	 */
	public function getHasPrevPage()
	{
		return ($this->page > 1);
	}

	/**
	 * @return int
	 */
	public function getPageSize()
	{
		return $this->pageSize;
	}

	/**
	 * @return int
	 */
	public function setPageSize($size)
	{
		$this->pageSize = $size;
	}

	/**
	 * 次ページ送り
	 */
	public function nextPage()
	{
		if ($this->getHasNextPage()) {
			$this->page++;
		} else {
			$message = "無効なメソッド呼び出し nextPage";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 * 前ページ送り
	 */
	public function prevPage()
	{
		if ($this->getHasPrevPage()) {
			$this->page--;
		} else {
			$message = "無効なメソッド呼び出し previousPage";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 * 現在のページオフセットを設定する
	 * @param int $page
	 */
	public function setCurrentPage($page)
	{
		$this->page = $page;
	}

	/**
	 * 現在のページオフセットを返す
	 * @return int
	 */
	public function getCurrentPage()
	{
		return $this->page;
	}

	/**
	 * 総件数を返す
	 * @return int
	 */
	public function getTotalCount()
	{
		return $this->totalCount;
	}

	/**
	 * 総ページ数を返す
	 * @return int
	 */
	public function getTotalPage()
	{
		return ceil($this->totalCount / $this->pageSize);
	}

	/**
	 * @return int
	 */
	public function getEndLessPage()
	{
		$startPage = 0;
		if ($this->groupsPage === null) {
			$startPage = 1;
		} else {
			$totalPages = $this->getTotalPage();
			$startPage = ($this->groupsPage * ceil($this->currentPage / $this->groupsPage) -
			($this->groupsPage - 1));
		}
		return ($startPage > 1 ? ($startPage - 1) : 1) ;
	}

	/**
	 * @return int
	 */
	public function getStartMorePage()
	{
		$totalPages = $this->getTotalPage();
		$startPage = ($this->groupsPage * ceil($this->currentPage / $this->groupsPage) -
		($this->groupsPage - 1));
		return (($startPage + $this->groupsPage) > $totalPages ? $totalPages : ($startPage + $this->groupsPage))  ;
	}

	/**
	 * @return int
	 */
	public function getEndMorePage()
	{
		//		if ($this->groupsPage == null) {
		//			// TODO : Exception
		//		} else {
		//			return int($this->currentPage / $this->groupsPage);
		//		}
	}

	/**
	 * @return int
	 */
	public function getHasMoreGroup()
	{
		if ($this->groupsPage === null) {
			return false;
		} else {
			$total = $this->getTotalPage();
			return (ceil($total / $this->groupsPage) > ceil($this->currentPage / $this->groupsPage));
		}
	}

	/**
	 * @return int
	 */
	public function getHasLessGroup()
	{
		if ($this->groupsPage === null) {
			return false;
		} else {
			return ($this->groupsPage < $this->currentPage);
		}
	}

	//protected $hasLessGroup;
	//protected $hasMoreGroup;
	//protected $hasPreviousPage;
	//protected $hasNextPage;
	//protected $startMorePage;
	//protected $endMorePage;
	//protected $startLessPage;
	//protected $endLessPage;
}
